package edu.uta.cfl.engine.constraint;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;

import choco.Choco;
import choco.cp.model.CPModel;
import choco.cp.solver.CPSolver;
import choco.kernel.model.Model;
import choco.kernel.model.constraints.Constraint;
import choco.kernel.model.variables.integer.IntegerExpressionVariable;
import choco.kernel.model.variables.integer.IntegerVariable;
import choco.kernel.solver.Configuration;
import choco.kernel.solver.ContradictionException;
import choco.kernel.solver.Solution;
import choco.kernel.solver.Solver;
import choco.kernel.solver.search.ISolutionPool;
import edu.uta.cfl.data.Combo;
import edu.uta.cfl.data.Component;
import edu.uta.cfl.data.Parameter;

public class ChocoContext {

	public static boolean DONNOT_REMOVE_MISSING = true;
	public static boolean SORT_IN_EXPAND = true;
	public static boolean USE_CONSTRAINT_GROUP = true;
	public static boolean USE_DIRTY_PARAM = true;
	public static boolean USE_INVALID_LIST = true;

	// choco on or off
	public static boolean USE_CHOCO_SOLVER = true;
	public static boolean USE_WHITELIST = true;

	// map for enum variables
	ArrayList<String> enumList;
	// constraints used in Choco
	ArrayList<Constraint> constraints;

	// ChocoVariable
	ArrayList<ChocoVariable> chocoVariables;
	// disjoint constraint Groups
	ArrayList<HashSet<Constraint>> consGroup;
	ArrayList<String> paraNames;

	// the related constraint Groups index for all variables, mainly for test[]
	int[] varGroupID;
	int[] innerID;
	// how many variables are evolved in constraint group [i]
	int[] groupSize;

	// used for solver
	ArrayList<ArrayList<Integer>> contex;
	ArrayList<HashSet<ArrayList<Integer>>> invalidList;
	ArrayList<HashSet<ArrayList<Integer>>> validList;

	HashMap<Integer, int[][]> whiteLists = new HashMap<Integer, int[][]>();
	// a temporary set for capture variables used when parsing a constraint
	// string
	private HashSet<IntegerVariable> varInUse;

	// record calling times
	int[] countSolverCall;
	int[] countContexCall;
	int[] countTrueCall;
	int countTrue;
	int countEval;

	public void printInfo() {

		/*
		 * for(ChocoVariable cv:chocoVariables){ cv.printInfo(); }
		 */

		System.out.println("==========================================");
		System.out.println("DONNOT_REMOVE_MISSING\t" + DONNOT_REMOVE_MISSING);
		System.out.println("SORT_FIRST_IN_EXPAND\t" + SORT_IN_EXPAND);
		System.out.println("USE_CONSTRAINT_GROUP\t" + USE_CONSTRAINT_GROUP);
		System.out.println("USE_DIRTY_PARAM \t" + USE_DIRTY_PARAM);
		System.out.println("USE_INVALID_LIST\t" + USE_INVALID_LIST);
		System.out.println("==========================================");
		int countAll = 0;
		int totalContexCall = 0;
		System.out.println("ID\t#True\t#Solve\t#Call");
		String[] s = new String[countContexCall.length];
		s[0] = "check tuple in grow(), pass invalid tuples";
		s[5] = "buildInitialMatrix";
		s[6] = "carefy, fill DONTCARE values.";
		s[7] = "carefy, fill DONTCARE values.";
		s[8] = "carefy, fill DONTCARE values.";
		s[9] = "check colomn values in expand().";
		s[10] = "getCoveredTuples for test with DONT_CARE values";
		s[11] = "satisfyAllConstraints in grow";
		s[12] = "check exist test when extend";
		s[14] = "coverage checker";
		s[17] = "removeInvalidTuples when building tuple tree";

		for (int i = 0; i < countContexCall.length; i++)
			if (countSolverCall[i] > 0) {
				System.out.println("" + i + "\t" + countTrueCall[i] + "\t"
						+ countSolverCall[i] + "\t" + countContexCall[i] + "\t"
						+ s[i]);
				countAll += countSolverCall[i];
				totalContexCall += countContexCall[i];
			}

		System.out.println();
		int n1 = 0, n2 = 0;
		for (HashSet<ArrayList<Integer>> t1 : invalidList) {
			n1 += t1.size();
		}
		for (HashSet<ArrayList<Integer>> t2 : validList) {
			n2 += t2.size();
		}
		float avg = 0;
		System.out.println("Parameter group size: ");
		for (int i : groupSize) {
			System.out.print(i + ", ");
			avg += i;
		}
		System.out.println(" Avg size: " + avg / groupSize.length);

		System.out.println("Valid list: " + n2 + ", Invalid List: " + n1);
		System.out.println("Solver Return True/False    : " + countTrue + " / "
				+ (countAll - countTrue));
		System.out.println("Total Eval/Context/Solver Called : " + countEval
				+ " / " + totalContexCall + " / " + (countAll));
	}

	public ChocoContext() {
		// System.out.println("new Choco Context>>>>>>>>");
		varInUse = new HashSet<IntegerVariable>();
		countTrue = 0;
		countEval = 0;
		countSolverCall = new int[32];
		countContexCall = new int[32];
		countTrueCall = new int[32];
	}

	// initial Choco and solve current problem. Do propagate to find new domain
	public boolean initChoco(ArrayList<Parameter> params, ArrayList<String> cons) {
		// System.out.println("Initiate Choco>>>>>>>>");
		HashMap<String, Parameter> paramsMap = initialParameters(params);
		HashMap<IntegerVariable, HashSet<Constraint>> v2c = initialConstraints(
				paramsMap, cons);
		groupVariables(v2c);
		boolean ret = solveSUT();
		if (ret) {
			if (USE_WHITELIST)
				solveSubProblem();
		}

		// initial valid and invalid list
		invalidList = new ArrayList<HashSet<ArrayList<Integer>>>(
				consGroup.size());
		validList = new ArrayList<HashSet<ArrayList<Integer>>>(consGroup.size());

		for (int i = 0; i < consGroup.size(); i++) {
			invalidList.add(new HashSet<ArrayList<Integer>>());
			validList.add(new HashSet<ArrayList<Integer>>());
		}

		contex = new ArrayList<ArrayList<Integer>>(consGroup.size());

		return ret;
	}

	public HashMap<String, Parameter> initialParameters(
			ArrayList<Parameter> params) {
		HashMap<String, Parameter> paramsMap = new HashMap<String, Parameter>();
		HashSet<String> strings = new HashSet<String>();

		chocoVariables = new ArrayList<ChocoVariable>(params.size());
		paraNames = new ArrayList<String>(params.size());

		for (Parameter p : params) {
			if (p.getParamType() == Parameter.PARAM_TYPE_ENUM) {
				strings.addAll(p.getValues());
			}
		}
		enumList = new ArrayList<String>(strings);
		Collections.sort(enumList);

		// load parameters
		for (Parameter p : params) {
			paramsMap.put(p.getName(), p);
			paraNames.add(p.getName());

			ArrayList<Integer> values = new ArrayList<Integer>();
			if (p.getParamType() == Parameter.PARAM_TYPE_INT) {
				for (String v : p.getValues()) {
					values.add(Integer.parseInt(v));
				}
				chocoVariables.add(new ChocoVariable(p, values));
			} else if (p.getParamType() == Parameter.PARAM_TYPE_BOOL) {
				values.add(1);
				values.add(0);
				chocoVariables.add(new ChocoVariable(p, values));
			} else if (p.getParamType() == Parameter.PARAM_TYPE_ENUM) {
				for (String v : p.getValues()) {
					values.add(enumList.indexOf(v));
				}
				chocoVariables.add(new ChocoVariable(p, values));
			}
		}
		return paramsMap;
	}

	private HashMap<IntegerVariable, HashSet<Constraint>> initialConstraints(
			HashMap<String, Parameter> paramsMap, ArrayList<String> cons) {

		constraints = new ArrayList<Constraint>(cons.size());

		// constraint-Variable map
		HashMap<Constraint, HashSet<IntegerVariable>> c2v = new HashMap<Constraint, HashSet<IntegerVariable>>();
		HashMap<IntegerVariable, HashSet<Constraint>> v2c = new HashMap<IntegerVariable, HashSet<Constraint>>();
		// parse constraints to Choco
		for (String s : cons) {
			// System.out.println(s);
			Constraint c = null;
			ConstraintChocoParser parser = new ConstraintChocoParser(s,
					paramsMap, this, null);
			varInUse.clear();// capture variables used in each constraints
			try {
				c = parser.parse();
			} catch (Exception ex) {
				System.out.println(ex);
			}
			if (c == null) {
				System.out.println("errors in parsing constraints!");
				System.exit(1);
			}
			// System.out.println("Parsed constraint: "+ s +"\n"+c.pretty());
			constraints.add(c);

			HashSet<IntegerVariable> vset = new HashSet<IntegerVariable>(
					varInUse);
			c2v.put(c, vset);
			for (IntegerVariable v : vset) {
				if (v2c.containsKey(v)) {
					v2c.get(v).add(c);
				} else {
					v2c.put(v, new HashSet<Constraint>(Arrays.asList(c)));
				}
			}
		}

		// propagate relations
		boolean changed = true;
		while (changed) {
			changed = false;
			for (Entry<IntegerVariable, HashSet<Constraint>> entry : v2c
					.entrySet()) {
				IntegerVariable currV = entry.getKey();
				HashSet<Constraint> currC = entry.getValue();
				HashSet<Constraint> temp = new HashSet<Constraint>(currC);
				for (Constraint c : currC) {
					for (IntegerVariable v : c2v.get(c)) {
						if (v != currV) {
							HashSet<Constraint> newC = v2c.get(v);
							if (temp.addAll(newC))
								changed = true;
						}
					}
				}
				currC.addAll(temp);
			}
		}

		return v2c;
	}

	private void groupVariables(
			HashMap<IntegerVariable, HashSet<Constraint>> v2c) {

		consGroup = new ArrayList<HashSet<Constraint>>();

		// group variables
		for (Entry<IntegerVariable, HashSet<Constraint>> entry : v2c.entrySet()) {
			IntegerVariable currV = entry.getKey();
			HashSet<Constraint> currC = entry.getValue();
			int varID = 0;
			for (varID = 0; varID < chocoVariables.size(); varID++) {
				if (chocoVariables.get(varID).var == currV)
					break;
			}

			int groupID = consGroup.indexOf(currC);
			if (groupID == -1) {
				consGroup.add(currC);
				groupID = consGroup.indexOf(currC);
			}
			chocoVariables.get(varID).setGroupID(groupID);
		}

		// build group arrays
		groupSize = new int[consGroup.size()];
		varGroupID = new int[chocoVariables.size()];
		innerID = new int[chocoVariables.size()];

		for (int i = 0; i < chocoVariables.size(); i++) {
			varGroupID[i] = chocoVariables.get(i).getGroupID();
			if (varGroupID[i] >= 0)
				groupSize[varGroupID[i]]++;
		}

	}

	public boolean solveSUT() {
		Model model = new CPModel();

		for (Constraint c : constraints) {
			// System.out.println(c.pretty());
			model.addConstraint(c);
		}
		Solver solver = new CPSolver();
		solver.read(model);

		try {
			solver.propagate(); // find new domain for each variable
			for (ChocoVariable cv : chocoVariables) {
				if (solver.getVar(cv.var) != null) {
					cv.setNewDomain(solver.getVar(cv.var).getDomain());
					// System.out.println(cv.var.getName()+" = " +
					// solver.getVar(cv.var).getDomain());
				}
			}
		} catch (ContradictionException e) {
			e.printStackTrace();
			System.out.println("Constraints is not solvable.");
			return false;
		}
		return true;
	}

	public void solveSubProblem() {
		for (int i = 0; i < consGroup.size(); i++) {
			Model model = new CPModel();
			int max = 1;
			ArrayList<IntegerVariable> iv = new ArrayList<IntegerVariable>();

			for (int k = 0; k < chocoVariables.size(); k++) {
				ChocoVariable v = chocoVariables.get(k);
				if (i == v.getGroupID()) {
					max = max * v.var.getDomainSize();
					iv.add(v.var);
					// System.out.print(v.name+" ");
					innerID[k] = iv.size() - 1;
				}

			}
			// System.out.println();

			if (max > 1e5) {
				continue;
			}

			for (Constraint c : consGroup.get(i)) {
				model.addConstraint(c);
			}

			Solver solver = new CPSolver();
			solver.read(model);

			int size = Integer.MAX_VALUE;
			solver.getConfiguration().putInt(
					Configuration.SOLUTION_POOL_CAPACITY, size);
			solver.solveAll();
			ISolutionPool pool = solver.getSearchStrategy().getSolutionPool();

			if (pool.size() > 1e4)
				continue;

			// System.out.println("Constraint Group "+i+": "+solver.getSolutionCount()+"/"+max);

			ArrayList<Integer> solverId = new ArrayList<Integer>();
			for (int k = 0; k < iv.size(); k++) {
				solverId.add(solver.getIntVarIndex(solver.getVar(iv.get(k))));
			}

			int[][] whiteList = new int[pool.size()][iv.size()];

			for (int j = 0; j < pool.size(); j++) {
				Solution s = pool.asList().get(j);
				for (int k = 0; k < iv.size(); k++) {
					whiteList[j][k] = s.getIntValue(solverId.get(k));
					// System.out.print(whiteList[j][k]+" ");
				}
				// System.out.println();
			}

			whiteLists.put(i, whiteList);
			// System.out.println("============");
		}
	}

	// 1 tuple (pv paires)
	public boolean isSatisfied(Combo combo, int tag) {
		countContexCall[tag]++;
		contex.clear();
		for (int i = 0; i < consGroup.size(); i++) {
			contex.add(null);
		}
		// Iterator<?> it = tuple.getIterator();
		// while(it.hasNext()){
		for (Component cp : combo.getComponent()) {
			int i = paraNames.indexOf(cp.getParameter().getName());
			int n = varGroupID[i];
			if (n != -1) {
				addContex(contex, n, i, cp.getValue());
			}
		}
		return prepareSolver(contex, tag);
	}

	// 2 test array
	public boolean isSatisfied(int[] test, int tag) {
		countContexCall[tag]++;
		contex.clear();
		for (int i = 0; i < consGroup.size(); i++) {
			contex.add(null);
		}

		for (int i = 0; i < test.length; i++) {
			if (test[i] != -1) {
				int n = varGroupID[i];
				if (n != -1) {
					addContex(contex, n, i, test[i]);
				}
			}
		}
		return prepareSolver(contex, tag);
	}

	// 3 with dirty parameters
	public boolean isSatisfied(int[] test, int[] cols, int tag) {
		if (false == USE_DIRTY_PARAM) {
			return isSatisfied(test, tag);
		}
		boolean needsolve = false;
		for (int i = 0; i < cols.length; i++) {// add pos
			if (cols[i] == 1) {
				int n = varGroupID[i];
				if (n != -1) {
					needsolve = true;
					break;
				}
			}
		}
		if (!needsolve)
			return true;

		countContexCall[tag]++;
		contex.clear();
		for (int i = 0; i < consGroup.size(); i++) {
			contex.add(null);
		}
		for (int i = 0; i < cols.length; i++) {// add pos
			if (cols[i] == 1) {
				int n = varGroupID[i];
				if (test[i] != -1 && n != -1) {
					addContex(contex, n, i, test[i]);
				}
			}
		}

		for (int i = 0; i < test.length; i++) {// add remaining
			if (test[i] != -1 && cols[i] != 1) {
				int n = varGroupID[i];
				if (n != -1) {
					if (contex.get(n) != null) {
						addContex(contex, n, i, test[i]);
					}
				}
			}
		}
		return prepareSolver(contex, tag);
	}

	public void addContex(ArrayList<ArrayList<Integer>> contex, int groupID,
			int varID, int value) {

		if (groupID < 0 || value < 0)
			return;

		if (contex.get(groupID) != null) {
			contex.get(groupID).add(varID);
			contex.get(groupID).add(value);
		} else {
			ArrayList<Integer> list = new ArrayList<Integer>();
			list.add(varID);
			list.add(value);
			contex.set(groupID, list);
		}
	}

	public boolean prepareSolver(ArrayList<ArrayList<Integer>> contex, int tag) {

		boolean needSolve = false;
		boolean result = true;

		for (int groupID = 0; groupID < contex.size(); groupID++) {
			ArrayList<Integer> pairs = contex.get(groupID);
			if (pairs == null)
				continue;

			for (int i = 0; i < pairs.size(); i += 2) {
				ChocoVariable cv = chocoVariables.get(pairs.get(i));
				int value = cv.getValue(pairs.get(i + 1));
				if (!cv.checkValue(value))
					return false;
			}

			if (USE_CONSTRAINT_GROUP && pairs.size() / 2 == 1)
				continue;

			// Linbin : disabled for now since it depends on evaluation method.
			// Need to rewrite evaluate().
			// if(USE_CONSTRAINT_GROUP && pairs.size()/2==groupSize[groupID])
			// continue;

			if (USE_INVALID_LIST && invalidList.get(groupID).contains(pairs))
				return false;
			if (USE_INVALID_LIST && validList.get(groupID).contains(pairs))
				continue;

			if (USE_WHITELIST && whiteLists.get(groupID) != null) {
				boolean r = searchWhiteList(pairs, whiteLists.get(groupID));

				if (r == false) {
					invalidList.get(groupID).add(pairs);
					return false;
				} else if (r == true) {
					validList.get(groupID).add(pairs);
					continue;
				}
			}

			needSolve = true;

			// System.out.println(countContexCall[5]+" "+pairs.toString());

			if (doSolve(groupID, pairs) == false) {
				result = false;
				break;
			}
		}

		if (needSolve) {
			countTrue = (result) ? (countTrue + 1) : countTrue;
			countTrueCall[tag] = (result) ? (countTrueCall[tag] + 1)
					: countTrueCall[tag];
			countSolverCall[tag]++;
		} else {
			if (USE_CONSTRAINT_GROUP == false)
				countSolverCall[tag]++;
		}

		return result;
	}

	public boolean doSolve(int groupID, ArrayList<Integer> pairs) {
		boolean result = true;
		CPModel m = new CPModel();

		// constraints in the group
		for (Constraint c : consGroup.get(groupID)) {
			m.addConstraint(c);
		}

		// fixed value for a parameter, as a constraint
		for (int i = 0; i < pairs.size(); i += 2) {
			ChocoVariable cv = chocoVariables.get(pairs.get(i));
			int value = cv.getValue(pairs.get(i + 1));
			if (!cv.checkValue(value))
				return false;
			m.addConstraint(Choco.eq(cv.var, value));
		}

		Solver sol = new CPSolver();
		sol.read(m);
		result = sol.solve();

		if (result == false) {
			invalidList.get(groupID).add(pairs);
		} else if (result == true) {
			validList.get(groupID).add(pairs);
		}

		return result;
	}

	public boolean searchWhiteList(ArrayList<Integer> pairs, int[][] list) {
		int i = 0, j = 0, id = 0;
		try {
			for (i = 0; i < list.length; i++) {
				boolean match = true;
				for (j = 0; j < pairs.size(); j += 2) {
					id = innerID[pairs.get(j)];
					if (list[i][id] != pairs.get(j + 1)) {
						match = false;
						break;
					}
				}
				if (match)
					return true;
			}
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println(e.toString());
			System.out.println(pairs.toString());
			System.out.println(list.toString());
			System.out.println("i: " + i + " j: " + j + " id: " + id);
			return false;
		}

		return false;
	}

	// =========================================for constraint
	// parser============================

	public IntegerExpressionVariable mk_enum(String s) {
		int v = enumList.indexOf(s);
		if (v < 0) {
			enumList.add(s);
			System.out.println("Warning: No domain contains this enum value - "
					+ s);
			return mk_int(enumList.size() - 1);
		}
		return mk_int(v);
	}

	public IntegerVariable mk_int(int v) {
		// System.out.println("make int constr "+v);
		return Choco.constant(v);
	}

	public IntegerVariable mk_int_var(String s) {
		for (ChocoVariable cv : chocoVariables)
			if (s.equals(cv.name)) {
				// System.out.println("exsiting var "+s);
				varInUse.add(cv.var);
				return cv.var;
			}
		return null;
	}

	public IntegerVariable mk_bool_var(String s) {
		return mk_int_var(s);
	}

	public IntegerVariable mk_true() {
		// System.out.println("make Choco.TRUE");
		return Choco.constant(1);
	}

	public IntegerVariable mk_false() {
		// System.out.println("make Choco.FALSE");
		return Choco.constant(0);
	}

	public IntegerExpressionVariable mk_op(String op,
			IntegerExpressionVariable oprnd1, IntegerExpressionVariable oprnd2) {
		// System.out.println("make operation "+oprnd1+op+oprnd2);
		if (op.equalsIgnoreCase(PLUS))
			return Choco.plus(oprnd1, oprnd2);
		if (op.equalsIgnoreCase(MUL))
			return Choco.mult(oprnd1, oprnd2);
		if (op.equalsIgnoreCase(MINUS))
			return Choco.minus(oprnd1, oprnd2);
		if (op.equalsIgnoreCase(DIV))
			return Choco.div(oprnd1, oprnd2);
		if (op.equalsIgnoreCase(MOD))
			return Choco.mod(oprnd1, oprnd2);
		return null;
	}

	public Constraint mk_comp(String op, IntegerExpressionVariable oprnd1,
			IntegerExpressionVariable oprnd2) {
		// System.out.println("make comparison "+oprnd1+op+oprnd2);
		if (op.equalsIgnoreCase(EQ) || op.equalsIgnoreCase(EQ2))
			return Choco.eq(oprnd1, oprnd2);
		if (op.equalsIgnoreCase(NEQ))
			return Choco.neq(oprnd1, oprnd2);
		if (op.equalsIgnoreCase(LT))
			return Choco.lt(oprnd1, oprnd2);
		if (op.equalsIgnoreCase(LE))
			return Choco.leq(oprnd1, oprnd2);
		if (op.equalsIgnoreCase(GT))
			return Choco.gt(oprnd1, oprnd2);
		if (op.equalsIgnoreCase(GE))
			return Choco.geq(oprnd1, oprnd2);
		return null;
	}

	public Constraint mk_cst(String op, Constraint oprnd1, Constraint oprnd2) {
		// System.out.println("make constraint "+op);
		if (op.equalsIgnoreCase(NOT))
			return (Constraint) Choco.not(oprnd2);
		if (op.equalsIgnoreCase(AND))
			return (Constraint) Choco.and(oprnd1, oprnd2);
		if (op.equalsIgnoreCase(OR))
			return (Constraint) Choco.or(oprnd1, oprnd2);
		if (op.equalsIgnoreCase(IMP))
			return Choco.implies(oprnd1, oprnd2);
		return null;
	}

	/*
	 * public static void main(String[] args) { unitTest(); }
	 * 
	 * public static void unitTest() {
	 * System.out.println("unitTest Choco>>>>>>>> ");
	 * 
	 * Model m = new CPModel(); // Build enumerated domain variables
	 * IntegerVariable[] p=new IntegerVariable[5]; for (int i=0;i<p.length;i++){
	 * p[i]=Choco.makeIntVar("p"+(i), 0, 1); m.addVariable(p[i]); } // Build the
	 * constraints Constraint C1 = Choco.eq(p[0],1); Constraint C2 =
	 * Choco.or(Choco.eq(p[1],1), Choco.eq(p[2],1)); m.addConstraint(C1);
	 * m.addConstraint(C2); // Build a solver and read model Solver s = new
	 * CPSolver(); s.read(m);
	 * 
	 * //find domain for each variable try{ s.propagate(); for (int
	 * i=0;i<p.length;i++){ System.out.println("p"+i+" = " +
	 * s.getVar(p[i]).getDomain()); } } catch (ContradictionException e){
	 * e.printStackTrace(); } }
	 */

	static String SYSTEM_MODIFIED = "Modify";
	static String PARAM_TYPE_BOOL = "@bool@";
	static String PARAM_TYPE_INT = "@int@";
	static String PARAM_TYPE_ENUM = "@enum@";

	static String FALSE = "false";
	static String TRUE = "true";
	static String DIGIT = "\\d*";
	static String LETTER = "\\w*";
	static String MOD = "%";
	static String DIV = "/";
	static String MUL = "*";
	static String MINUS = "-";
	static String PLUS = "+";
	static String NOT_str = "NOT";
	static String NOT = "!";
	static String OR_str = "OR";
	static String OR = "||";
	static String OR_regex = "|";
	static String NE = "-";
	static String GE = ">=";
	static String GE_STR = "GE";
	static String LE = "<=";
	static String LE_STR = "LE";
	static String LT = "<";
	static String GT = ">";
	static String EQ2 = "==";
	static String EQ = "=";
	static String AND = "&&";
	static String AND_str = "AND";
	static String AND_regex = "&";
	static String IMP = "=>";
	static String IMP_STR = "\"=>\"";
	static String NEQ = "!=";
	static String NEQ_STR = "NEQ";
	static String OPEN_PAR = "(";
	static String CLOSE_PAR = ")";
	static String BSLASH = "\\";
	static String SPACE = " ";
	static String DOUBLEQUOTE = "\"";
}
